package com.doge.service.auth;

import com.doge.bean.auth.*;
import com.doge.bean.auth.WxRegisterBO;
import com.doge.bean.pojo.admin.Admin;
import com.doge.bean.pojo.admin.AdminExample;
import com.doge.bean.pojo.permission.Permission;
import com.doge.bean.pojo.permission.PermissionExample;
import com.doge.bean.pojo.permission.PermissionList;
import com.doge.bean.pojo.permission.PermissionListExample;
import com.doge.bean.pojo.role.Role;
import com.doge.bean.pojo.role.RoleExample;
import com.doge.bean.pojo.user.User;
import com.doge.bean.pojo.user.UserExample;
import com.doge.exception.Param702Exception;
import com.doge.exception.ServerException;
import com.doge.mapper.*;
import com.doge.util.auth.GetSubject;
import com.doge.util.properties.AliyunUtilProperties;
import com.doge.util.valid.ParamValidation;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @program: doge
 * @description:
 * @author: Keyu Li
 * @create: 2021-12-02 14:58
 **/


@Service
public class AuthServiceImpl implements AuthService {
    @Autowired
    AdminMapper adminMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    RoleMapper roleMapper;
    @Autowired
    PermissionMapper permMapper;
    @Autowired
    PermissionListMapper permListMapper;
    @Autowired
    AliyunUtilProperties aliyun;

    @SneakyThrows
    @Override
    public AdminLoginData loginAdmin(MarketToken token) {
        AdminExample example = new AdminExample();
        example.createCriteria().andUsernameEqualTo(token.getUsername()).andDeletedEqualTo(false);
        List<Admin> admins = adminMapper.selectByExample(example);
        if (admins.size() == 0) {
            // 没有查询到该用户
            throw new UnknownAccountException();
        }
        if (admins.size() > 1) {
            // 理论上 username 不可重复，如果返回数据超过1条，说明数据库异常！
            throw new ServerException("数据库中有重复的用户名！");
        }
        // 查询到 1 个用户则是正常情况，赋予token type和id
        Admin admin = admins.get(0);
        token.setType("admin");
        token.setUser(admin);
        // 取subject
        Subject subject = SecurityUtils.getSubject();
        // 登录（接下来是走到 authentication，然后是 AdminRealm）
        // 如果登录失败，直接抛出异常了
        subject.login(token);
        // 这个时候token理论上已经有id了？

        // loginData 赋值
        InfoBean info = new InfoBean();
        info.setAvatar(admin.getAvatar());
        info.setNickName(admin.getUsername());
        AdminLoginData loginData = new AdminLoginData();
        loginData.setAdminInfo(info);
        loginData.setToken((String) subject.getSession().getId());
        // 更新 登录ip和时间
        admin.setLastLoginIp(subject.getSession().getHost());
        admin.setLastLoginTime(new Date());
        adminMapper.updateByPrimaryKeySelective(admin);
        return loginData;
    }

    @SneakyThrows
    @Override
    public UserLoginData loginWx(MarketToken token) {
        // TODO: weixin_openid 暂时没有办法解决
        UserExample example = new UserExample();
        example.createCriteria().andUsernameEqualTo(token.getUsername()).andDeletedEqualTo(false);
        List<User> users = userMapper.selectByExample(example);
        if (users.size() == 0) {
            // 没有查询到该用户
            throw new UnknownAccountException();
        }
        if (users.size() > 1) {
            // 理论上 username 不可重复，如果返回数据超过1条，说明数据库异常！
            throw new ServerException("无法确定当前用户，数据库中有重复的用户名！");
        }
        // 查询到 1 个用户则是正常情况，赋予token type和id
        User user = users.get(0);
        token.setType("wx");
        token.setUser(user);
        // 取subject
        Subject subject = SecurityUtils.getSubject();
        // 登录（接下来是走到 authentication，然后是 AdminRealm）
        // 如果登录失败，直接抛出异常了
        subject.login(token);
        // 这个时候token理论上已经有id了？

        // loginData 赋值
        InfoBean info = new InfoBean();
        info.setAvatar(user.getAvatar());
        info.setNickName(user.getUsername());
        UserLoginData loginData = new UserLoginData();
        loginData.setUserInfo(info);
        loginData.setToken((String) subject.getSession().getId());
        // 更新 session_key
        user.setSessionKey(loginData.getToken());
        userMapper.updateByPrimaryKeySelective(user);
        return loginData;
    }


    @Override
    public InfoData getAdminInfo(String token) {
        InfoData infoData = new InfoData();
        // 获取admin对象，name，avatar赋值
        Admin admin = GetSubject.getAdmin();
        infoData.setName(admin.getUsername());
        infoData.setAvatar(admin.getAvatar());
        // 通过 role_ids 获取 roles，赋值
        RoleExample example = new RoleExample();
        List<Integer> roleIds = Arrays.asList(admin.getRoleIds());
        example.createCriteria().andIdIn(roleIds).andDeletedEqualTo(false);
        List<Role> roles = roleMapper.selectByExample(example);
        List<String> roleNames = new ArrayList<>();
        for (Role role : roles) {
            roleNames.add(role.getName());
        }
        infoData.setRoles(roleNames);
        // 通过 roleIds获取permissions
        // 判断 是否包含 超级管理员
        if (roleIds.contains(1) && roleNames.contains("超级管理员")) {
            ArrayList<String> perms = new ArrayList<>();
            perms.add("*");
            infoData.setPerms(perms);
            return infoData;
        }
        // 不包含超级管理员，则查询所有 permissions,再根据 permission 查询 api，日
        PermissionExample permExample = new PermissionExample();
        permExample.createCriteria().andRoleIdIn(roleIds).andDeletedEqualTo(false);
        List<Permission> permissions = permMapper.selectByExample(permExample);
        // 将所有 permission 放入一个 set 当中
        HashSet<String> permSets = new HashSet<>();
        for (Permission permission : permissions) {
            permSets.add(permission.getPermission());
        }
        //  根据permission查询api
        PermissionListExample listExample = new PermissionListExample();
        listExample.createCriteria().andPermissionIn(new ArrayList<>(permSets));
        List<PermissionList> permissionLists = permListMapper.selectByExample(listExample);
        //提取api
        List<String> apis = new ArrayList<>();
        for (PermissionList permissionList : permissionLists) {
            apis.add(permissionList.getApi());
        }
        infoData.setPerms(apis);
        return infoData;
    }

    String code;

    @SneakyThrows
    @Override
    public void getVerfCode(String mobile) {
        code = null;
        // 校验电话号码合法性
        if (!ParamValidation.phoneNumValId(mobile)) {
            throw new Param702Exception("手机号格式不正确！");
        }
        // 产生随机数
        Random rd = new Random();
        code = String.valueOf(rd.nextInt(9) + 1)
                + rd.nextInt(1000);
        System.out.println("验证码：" + code);
        //aliyun.sendMsg(mobile, code);
        // 将code存储到 user表中，并记录当前时间
        User user = new User();
        user.setMobile(mobile);
        user.setWeixinOpenid(code);
        user.setAddTime(new Date());
        user.setDeleted(true);
        // 判断当前数据库中是否已经有 发送过验证码 的手机号
        UserExample example = new UserExample();
        example.createCriteria().andMobileEqualTo(mobile).andUsernameIsNull().andDeletedEqualTo(true);
        List<User> users = userMapper.selectByExample(example);
        if (users.size() == 0) {
            userMapper.insertSelective(user);
        }else {
            user.setId(users.get(0).getId());
            userMapper.updateByPrimaryKeySelective(user);
        }
    }

    @SneakyThrows
    @Override
    public UserLoginData register(WxRegisterBO reg) {
        User regedUser = new User();
        User user = verify(reg, regedUser);
        if (regedUser.getUsername()!=null) {
            throw new Param702Exception("当前手机号已经被注册！");
        }
        // 插入用户名和密码
        user.setMobile(reg.getMobile());
        user.setLastLoginIp(GetSubject.getIp());
        ;
        user.setDeleted(false);
        user.setUsername(reg.getUsername());
        user.setPassword(reg.getPassword());
        user.setWeixinOpenid(reg.getWxCode());
        userMapper.updateByPrimaryKeySelective(user);
        // 直接登录
        MarketToken token = new MarketToken();
        token.setType("wx");
        token.setUser(user);
        token.setUsername(reg.getUsername());
        token.setPassword(reg.getPassword().toCharArray());
        return loginWx(token);
    }

    @SneakyThrows
    @Override
    public void reset(WxRegisterBO reg) {
        User regedUser = new User();
        verify(reg, regedUser);

        if (regedUser.getUsername()==null) {
            throw new Param702Exception("当前手机号未被注册！");
        }
        regedUser.setPassword(reg.getPassword());
        userMapper.updateByPrimaryKeySelective(regedUser);
    }

    @SneakyThrows
    private User verify(WxRegisterBO reg, User regedUser) {
        User user = null;
        if (!ParamValidation.phoneNumValId(reg.getMobile())) {
            throw new Param702Exception("手机号格式不正确！");
        }
        // 通过手机号查询：
        UserExample userExample = new UserExample();
        userExample.createCriteria().andMobileEqualTo(reg.getMobile());
        List<User> users = userMapper.selectByExample(userExample);
        if (users.size() == 0) {
            throw new Param702Exception("当前手机号未发送验证码！");
        }
        // 找到最近的发送验证码的数据
        long now = new Date().getTime();
        long interval = 5 * 60 * 1000; // 5min
        for (User value : users) {
            if (value.getUsername() != null && !value.getDeleted()) {
                BeanUtils.copyProperties(value,regedUser);
                regedUser = value;
                continue;
                //throw new Param702Exception("当前手机号已经被注册！");
            }
            long old = value.getAddTime().getTime();
            if (interval > (now - old)) {
                interval = (now - old);
                user = value;
            }
        }
        //是否超过5min
        if (user == null) {
            throw new Param702Exception("验证码超时！");
        }
        if (!user.getWeixinOpenid().equals(reg.getCode())) {
            throw new Param702Exception("验证码错误！");
        }
        return user;
    }
}
